package com.xmg.p2p.business.service.impl;

import com.xmg.p2p.base.domain.Account;
import com.xmg.p2p.base.domain.Userinfo;
import com.xmg.p2p.base.query.PageResult;
import com.xmg.p2p.base.service.IAccountService;
import com.xmg.p2p.base.service.IUserinfoService;
import com.xmg.p2p.base.util.BidConst;
import com.xmg.p2p.base.util.BitStatesUtils;
import com.xmg.p2p.base.util.UserContext;
import com.xmg.p2p.business.domain.*;
import com.xmg.p2p.business.mapper.*;
import com.xmg.p2p.business.query.BidRequestQueryObject;
import com.xmg.p2p.business.service.IAccountFlowService;
import com.xmg.p2p.business.service.IBidRequestService;
import com.xmg.p2p.business.service.ISystemAccountService;
import com.xmg.p2p.business.util.CalculatetUtil;
import com.xmg.p2p.business.util.DecimalFormatUtil;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.xmg.p2p.base.util.BidConst.*;

@Service
public class BidRequestServiceImpl implements IBidRequestService {
    @Autowired
    private BidRequestMapper bidRequestMapper;
    @Autowired
    private IUserinfoService userinfoService;
    @Autowired
    private IAccountService accountService;
    @Autowired
    private BidRequestAuditHistoryMapper bidRequestAuditHistoryMapper;
    @Autowired
    private BidMapper bidMapper;
    @Autowired
    private IAccountFlowService accountFlowService;
    @Autowired
    private ISystemAccountService systemAccountService;
    @Autowired
    private PaymentScheduleMapper paymentScheduleMapper;
    @Autowired
    private PaymentScheduleDetailMapper paymentScheduleDetailMapper;

    @Override
    public void update(BidRequest bidRequest) {
        int i = bidRequestMapper.updateByPrimaryKey(bidRequest);
        if (i == 0) {
            throw new RuntimeException("乐观锁错误 BidRequest:" + bidRequest.getId());
        }
    }

    @Override
    public boolean canApplyBindRequest(Long logininfoId) {
        Userinfo userinfo = userinfoService.findById(logininfoId);
        if (userinfo != null && userinfo.getIsBasicInfo()
                && userinfo.getIsRealAuth()
                && userinfo.getIsVedioAuth()
                && userinfo.getScore() >= BidConst.BASE_BORROW_SCORE
                && !userinfo.getIsBidRequestProcess()
        ) {
            return true;
        }
        return false;
    }

    @Override
    public void apply(BidRequest request) {
        Account account = accountService.getCurrent();
        //判断条件
        if (canApplyBindRequest(UserContext.getLogininfo().getId())
                && request.getBidRequestAmount().compareTo(BidConst.SMALLEST_BIDREQUEST_AMOUNT) >= 0
                && request.getBidRequestAmount().compareTo(account.getRemainBorrowLimit()) <= 0
                && request.getCurrentRate().compareTo(BidConst.SMALLEST_CURRENT_RATE) >= 0
                && request.getCurrentRate().compareTo(BidConst.MAX_CURRENT_RATE) <= 0
        ) {
            //创建一个新的 set参数
            BidRequest bidRequest = new BidRequest();
            bidRequest.setBidRequestAmount(request.getBidRequestAmount());
            bidRequest.setCurrentRate(request.getCurrentRate());
            bidRequest.setMonthes2Return(request.getMonthes2Return());
            bidRequest.setReturnType(request.getReturnType());
            bidRequest.setMinBidAmount(request.getMinBidAmount());
            bidRequest.setDisableDays(request.getDisableDays());//如果前端有校验,后端也校验!
            bidRequest.setTitle(request.getTitle());
            bidRequest.setDescription(request.getDescription());

            bidRequest.setCreateUser(UserContext.getLogininfo());
            bidRequest.setApplyTime(new Date());
            bidRequest.setBidRequestState(BidConst.BIDREQUEST_STATE_PUBLISH_PENDING);//忘记了
            //用工具类来计算
            bidRequest.setTotalRewardAmount(CalculatetUtil.calTotalInterest(bidRequest.getReturnType(), bidRequest.getBidRequestAmount(), bidRequest.getCurrentRate(), bidRequest.getMonthes2Return()));

            bidRequestMapper.insert(bidRequest);

            Userinfo current = userinfoService.getCurrent();
            current.addStatue(BitStatesUtils.OP_HAS_BIDREQUEST_PROCESS);
            userinfoService.update(current);

        }
    }

    @Override
    public PageResult queryPage(BidRequestQueryObject qo) {
        int i = bidRequestMapper.queryForCount(qo);
        if (i > 0) {
            List<BidRequest> items = bidRequestMapper.queryList(qo);
            return new PageResult(items, i, qo.getCurrentPage(), qo.getPageSize());
        }
        return PageResult.empty(qo.getPageSize());
    }

    /**
     * 后台发标前审核
     * 查询表,判断条件
     * 构建审核历史
     * <p>
     * 根据状态,设置用户,设置标的值
     **/
    @Override
    public void audit(long id, int state, String remark) {
        BidRequest br = bidRequestMapper.selectByPrimaryKey(id);
        if (br != null && br.getBidRequestState() == BidConst.BIDREQUEST_STATE_PUBLISH_PENDING) {
            //审核历史对象构建
            BidRequestAuditHistory history = new BidRequestAuditHistory();
            history.setState(state);
            history.setRemark(remark);
            history.setAuditTime(new Date());
            history.setApplyTime(br.getApplyTime());
            history.setAuditor(UserContext.getLogininfo());
            history.setApplier(br.getCreateUser());
            history.setBidRequestId(br.getId());
            history.setAuditType(BidRequestAuditHistory.FULL_AUDIT_1);
            bidRequestAuditHistoryMapper.insert(history);
            //审核通过,修改标的属性
            if (state == BidRequestAuditHistory.STATE_AUDIT) {
                br.setBidRequestState(BidConst.BIDREQUEST_STATE_BIDDING);
                br.setDisableDate(DateUtils.addDays(new Date(), br.getDisableDays()));
                br.setPublishTime(new Date());
                br.setNote(remark);
            } else if (state == BidRequestAuditHistory.STATE_REJECT) {
                //审核不通过,修改标的属性,修改用户的属性
                br.setBidRequestState(BIDREQUEST_STATE_PUBLISH_REFUSE);
                Userinfo userinfo = userinfoService.findById(br.getCreateUser().getId());
                userinfo.removeState(BitStatesUtils.OP_HAS_BIDREQUEST_PROCESS);
                userinfoService.update(userinfo);
            }
            update(br);

        }
    }

    @Override
    public BidRequest findById(Long id) {
        return bidRequestMapper.selectByPrimaryKey(id);
    }

    @Override
    public List<BidRequestAuditHistory> listByBidRequestId(Long id) {
        return bidRequestAuditHistoryMapper.listByBidRequestId(id);
    }

    @Override
    public List<BidRequest> listIndex(int i) {
        BidRequestQueryObject qo = new BidRequestQueryObject();
        qo.setBidRequestStates(new int[]{BidConst.BIDREQUEST_STATE_BIDDING, BidConst.BIDREQUEST_STATE_PAYING_BACK, BidConst.BIDREQUEST_STATE_COMPLETE_PAY_BACK});
        qo.setPageSize(i);
        qo.setOrderBy("bidRequestState");
        qo.setOrderType("asc");
        return bidRequestMapper.queryList(qo);
    }

    @Override
    public void bid(long bidRequestId, int amount) {
        /**
         * 业务逻辑
         * 1.校验
         *      借款的状态
         *      投标金额大于最小投标金额
         *      投标金额小于当前已投标总金额
         *      投标金额小于前台用户账户可用余额
         *      其他校验就不写了:
         *          投标完以后如果这个标的剩余金额小于最小投标金额,则不允许,必须这一次全部投完
         *          本次投标只能占借款总额的多少
         *          同一个标只能投标几次
         *          用户的多个认证情况**
         * 2.修改数据,保存数据库
         *      BidRequest  满了则要修改状态
         *      Account
         *      Bid
         *      AccountFlow
         **/
        BidRequest bidRequest = findById(bidRequestId);
        BigDecimal bigAmount = new BigDecimal(amount);
        Account currentAccount = accountService.getCurrent();
        if (bidRequest != null && bidRequest.getBidRequestState() == BidConst.BIDREQUEST_STATE_BIDDING
                && bidRequest.getMinBidAmount().compareTo(bigAmount) <= 0
                && bidRequest.getRemainAmount().compareTo(bigAmount) >= 0
                && currentAccount.getUsableAmount().compareTo(bigAmount) >= 0
                && !bidRequest.getCreateUser().getId().equals(UserContext.getLogininfo().getId())//当前用户不是借款的借款人;
        ) {
            bidRequest.setBidCount(bidRequest.getBidCount() + 1);
            bidRequest.setCurrentSum(bidRequest.getCurrentSum().add(bigAmount));
            //借款总金额等于已投标总金额 说明投满了,状态就变了
            if (bidRequest.getCurrentSum().compareTo(bidRequest.getBidRequestAmount()) == 0) {
                bidRequest.setBidRequestState(BIDREQUEST_STATE_APPROVE_PENDING_1);
            }
            update(bidRequest);

            currentAccount.setFreezedAmount(currentAccount.getFreezedAmount().add(bigAmount));
            currentAccount.setUsableAmount(currentAccount.getUsableAmount().subtract(bigAmount));
            accountService.update(currentAccount);

            Bid bid = new Bid();
            bid.setActualRate(bidRequest.getCurrentRate());
            bid.setAvailableAmount(bigAmount);
            bid.setBidRequestId(bidRequest.getId());
//            bid.setBidRequestState(bidRequest.getBidRequestState());数据库没有这个字段,只在domain里面才有,用来查询的
            bid.setBidRequestTitle(bidRequest.getTitle());
            bid.setBidTime(new Date());
            bid.setBidUser(UserContext.getLogininfo());
            bidMapper.insert(bid);

            accountFlowService.bid(currentAccount, bid);


        }
    }

    /**
     * 满标一审后台审核
     * 借款对象BidRequest
     * 账户对象Account
     * 历史审核对象
     **/
    @Override
    public void fullAudit1(Long id, int state, String remark) {
        BidRequest bidRequest = findById(id);
        if (bidRequest != null && bidRequest.getBidRequestState() == BIDREQUEST_STATE_APPROVE_PENDING_1) {
            BidRequestAuditHistory history = new BidRequestAuditHistory();
            history.setAuditType(BidRequestAuditHistory.FULL_AUDIT_1);
            history.setBidRequestId(bidRequest.getId());
            history.setApplier(bidRequest.getCreateUser());
            history.setApplyTime(new Date());
            history.setAuditor(UserContext.getLogininfo());
            history.setAuditTime(new Date());
            history.setRemark(remark);
            history.setState(state);
            bidRequestAuditHistoryMapper.insert(history);
            if (state == BidRequestAuditHistory.STATE_AUDIT) {
                bidRequest.setBidRequestState(BIDREQUEST_STATE_APPROVE_PENDING_2);
            } else {
                bidRequest.setBidRequestState(BIDREQUEST_STATE_REJECTED);
                //前台账户移除在借款中的状态
                Userinfo borrowUser = userinfoService.findById(bidRequest.getCreateUser().getId());
                borrowUser.removeState(BitStatesUtils.OP_HAS_BIDREQUEST_PROCESS);
                userinfoService.update(borrowUser);
                returnMoney(bidRequest);
            }
        }
        update(bidRequest);
    }

    @Override
    public void fullAudit2(Long id, int state, String remark) {
        BidRequest bidRequest = findById(id);
        if (bidRequest != null && bidRequest.getBidRequestState() == BIDREQUEST_STATE_APPROVE_PENDING_2) {
            BidRequestAuditHistory history = new BidRequestAuditHistory();
            history.setAuditType(BidRequestAuditHistory.FULL_AUDIT_2);
            history.setBidRequestId(bidRequest.getId());
            history.setApplier(bidRequest.getCreateUser());
            history.setApplyTime(new Date());
            history.setAuditor(UserContext.getLogininfo());
            history.setAuditTime(new Date());
            history.setRemark(remark);
            history.setState(state);
            bidRequestAuditHistoryMapper.insert(history);
            if (state == BidRequestAuditHistory.STATE_AUDIT) {

//            1借款
//                 1.1状态
                bidRequest.setBidRequestState(BIDREQUEST_STATE_PAYING_BACK);
//
//            2借款人
//                  2.1收款
                Account borrowAccount = accountService.findById(bidRequest.getCreateUser().getId());
//                      2.1.1余额增加
                borrowAccount.setUsableAmount(borrowAccount.getUsableAmount().add(bidRequest.getBidRequestAmount()));
//                      2.1.2流水记录
                accountFlowService.borrowSuccess(borrowAccount, bidRequest);
//                      2.1.3待还本息=借款金额+借款利息
                borrowAccount.setUnReturnAmount(borrowAccount.getUnReturnAmount().add(bidRequest.getBidRequestAmount().add(bidRequest.getTotalRewardAmount())));
//                      2.1.4信用额度
                borrowAccount.setRemainBorrowLimit(borrowAccount.getRemainBorrowLimit().subtract(bidRequest.getBidRequestAmount()));
//                  2.2移除借款人借款状态码
                Userinfo borrowUser = userinfoService.findById(bidRequest.getCreateUser().getId());
                borrowUser.removeState(BitStatesUtils.OP_HAS_BIDREQUEST_PROCESS);
                userinfoService.update(borrowUser);
//                  2.3支付借款手续费
                BigDecimal managementChargeFee = CalculatetUtil.calAccountManagementCharge(bidRequest.getBidRequestAmount());
//                      2.3.1可用余额减少
                borrowAccount.setUsableAmount(borrowAccount.getUsableAmount().subtract(managementChargeFee));
//                      2.3.2支付借款手续费流水
                accountFlowService.chargeBorrowFee(managementChargeFee, bidRequest, borrowAccount);
//                      2.3.3平台账户收取借款手续费 2.3.4平台流水
                systemAccountService.chargeBorrowFee(bidRequest, managementChargeFee);
//              3投资人
//               3.1遍历投标
                Map<Long, Account> updates = new HashMap<>();
                BigDecimal totalBidInterest = ZERO;
                for (int i = 1; i <= bidRequest.getBids().size(); i++) {
                    Bid bid = bidRequest.getBids().get(i - 1);
//                    3.2根据投标减少投资人的冻结金额
                    Long bidUserId = bid.getBidUser().getId();
                    Account bidAccount = updates.get(bidUserId);
                    if (bidAccount == null) {
                        bidAccount = accountService.findById(bidUserId);
                        updates.put(bidUserId, bidAccount);
                    }
                    bidAccount.setFreezedAmount(bidAccount.getFreezedAmount().subtract(bid.getAvailableAmount()));
                    //               3.3生成成功投标的流水
                    accountFlowService.bidSuccess(bid, bidAccount);
                    //               3.4计算代收利息和代收本金
                    bidAccount.setUnReceivePrincipal(bidAccount.getUnReceivePrincipal().add(bid.getAvailableAmount()));
                    BigDecimal bidInterest = ZERO;
                    if (i < bidRequest.getBids().size()) {
                        //代收利息=投标金额/借款总金额*借款总回报利息
                        bidInterest = bid.getAvailableAmount().divide(bidRequest.getBidRequestAmount(), CAL_SCALE, RoundingMode.HALF_UP)
                                .multiply(bidRequest.getTotalRewardAmount());
                        bidInterest = DecimalFormatUtil.formatBigDecimal(bidInterest, STORE_SCALE);
                        //累加
                        totalBidInterest = totalBidInterest.add(bidInterest);
                    } else {
                        bidInterest = bidRequest.getTotalRewardAmount().subtract(totalBidInterest);
                    }
                    bidAccount.setUnReceiveInterest(bidAccount.getUnReceiveInterest().add(bidInterest));

                }
//             4后续流程
//               还钱对象
//               收钱对象 其实就是还钱明细
                createPaymentSchedules(bidRequest);

                accountService.update(borrowAccount);
                for (Account account : updates.values()) {
                    this.accountService.update(account);
                }

            } else {
                bidRequest.setBidRequestState(BIDREQUEST_STATE_REJECTED);
                //前台账户移除在借款中的状态
                Userinfo borrowUser = userinfoService.findById(bidRequest.getCreateUser().getId());
                borrowUser.removeState(BitStatesUtils.OP_HAS_BIDREQUEST_PROCESS);
                userinfoService.update(borrowUser);
                returnMoney(bidRequest);
            }
        }
        update(bidRequest);
    }

    private void createPaymentSchedules(BidRequest br) {
        Date now = new Date();
        BigDecimal totalInterest = BidConst.ZERO;
        BigDecimal totalPrincipal = BidConst.ZERO;
        for (int i = 0; i < br.getMonthes2Return(); i++) {
            // 针对每一期创建一个还款计划对象
            PaymentSchedule ps = new PaymentSchedule();
            ps.setBidRequestId(br.getId());
            ps.setBidRequestTitle(br.getTitle());
            ps.setBidRequestType(br.getBidRequestType());
            ps.setBorrowUser(br.getCreateUser());
            ps.setDeadLine(DateUtils.addMonths(now, i + 1));

            if (i < br.getMonthes2Return() - 1) {
                // 计算这一期的总还款金额
                ps.setTotalAmount(CalculatetUtil.calMonthToReturnMoney(
                        br.getReturnType(), br.getBidRequestAmount(),
                        br.getCurrentRate(), i + 1, br.getMonthes2Return()));
                // 计算这一期的利息
                ps.setInterest(CalculatetUtil.calMonthlyInterest(
                        br.getReturnType(), br.getBidRequestAmount(),
                        br.getCurrentRate(), i + 1, br.getMonthes2Return()));
                // 计算这一期的本金
                ps.setPrincipal(ps.getTotalAmount().subtract(ps.getInterest()));
                totalInterest = totalInterest.add(ps.getInterest());
                totalPrincipal = totalPrincipal.add(ps.getPrincipal());
            } else {
                // 这一期利息
                ps.setInterest(br.getTotalRewardAmount()
                        .subtract(totalInterest));
                ps.setPrincipal(br.getBidRequestAmount().subtract(
                        totalPrincipal));
                ps.setTotalAmount(ps.getPrincipal().add(ps.getInterest()));
            }
            ps.setMonthIndex(i + 1);
            ps.setReturnType(br.getReturnType());
            // 处于待还状态
            ps.setState(BidConst.PAYMENT_STATE_NORMAL);
            paymentScheduleMapper.insert(ps);

            // 生成还款明细
            createPaymentScheduleDetail(ps, br);
        }
    }

    private void createPaymentScheduleDetail(PaymentSchedule ps, BidRequest br) {
        BigDecimal totalAmount = BidConst.ZERO;
        for (int i = 0; i < br.getBids().size(); i++) {
            Bid bid = br.getBids().get(i);
            // 针对每一个投标创建一个还款明细
            PaymentScheduleDetail psd = new PaymentScheduleDetail();
            psd.setBidAmount(bid.getAvailableAmount());
            psd.setBidId(bid.getId());
            psd.setBidRequestId(br.getId());
            psd.setDeadline(ps.getDeadLine());
            psd.setFromLogininfo(br.getCreateUser());

            if (i < br.getBids().size() - 1) {
                // 计算利息
                BigDecimal interest = DecimalFormatUtil.formatBigDecimal(
                        bid.getAvailableAmount().divide(br.getBidRequestAmount(), BidConst.CAL_SCALE, RoundingMode.HALF_UP)
                                .multiply(ps.getInterest()),
                        BidConst.STORE_SCALE);
                // 计算本金
                BigDecimal principal = DecimalFormatUtil.formatBigDecimal(
                        bid.getAvailableAmount().divide(br.getBidRequestAmount(), BidConst.CAL_SCALE, RoundingMode.HALF_UP)
                                .multiply(ps.getPrincipal()),
                        BidConst.STORE_SCALE);
                psd.setInterest(interest);
                psd.setPrincipal(principal);
                psd.setTotalAmount(interest.add(principal));
                totalAmount = totalAmount.add(psd.getTotalAmount());
            } else {
                psd.setTotalAmount(ps.getTotalAmount().subtract(totalAmount));
                // 计算利息
                BigDecimal interest = DecimalFormatUtil.formatBigDecimal(
                        bid.getAvailableAmount().divide(br.getBidRequestAmount(), BidConst.CAL_SCALE, RoundingMode.HALF_UP)
                                .multiply(ps.getInterest()),
                        BidConst.STORE_SCALE);
                psd.setInterest(interest);
                psd.setPrincipal(psd.getTotalAmount().subtract(psd.getInterest()));
            }
            psd.setMonthIndex(ps.getMonthIndex());
            psd.setPaymentScheduleId(ps.getId());
            psd.setReturnType(br.getReturnType());
            // 投资人
            psd.setToLogininfoId(bid.getBidUser().getId());

            paymentScheduleDetailMapper.insert(psd);
        }
    }

    private void returnMoney(BidRequest bidRequest) {
        //查询出对应的投标对象,依次还钱
        Map<Long, Account> accountMap = new HashMap<>();
        List<Bid> bids = bidRequest.getBids();
        for (Bid bid : bids) {
            Long accountId = bid.getBidUser().getId();
            Account bidAccount = accountMap.get(accountId);
            if (bidAccount == null) {
                bidAccount = accountService.findById(bid.getBidUser().getId());
                accountMap.put(accountId, bidAccount);
            }
            bidAccount.setUsableAmount(bidAccount.getUsableAmount().add(bid.getAvailableAmount()));
            bidAccount.setFreezedAmount(bidAccount.getFreezedAmount().subtract(bid.getAvailableAmount()));
            accountFlowService.returnMoney(bidAccount, bid);
        }
        for (Account account : accountMap.values()) {
            accountService.update(account);

        }

    }

}